// 手写源码汇总


// 1、大数相加
function bigSum (num1, num2) {

    let i = 0
    let len1 = num1.length
    let len2 = num2.length
    let plus = 0
    let result = []

    while(i<num1.length || i<num2.length){
        let n1 = num1[len1-1-i] || 0
        let n2 = num2[len2-1-i] || 0

        let sum = Number(n1) + Number(n2) + plus
        if(sum>=10){
            sum = sum%10
            plus = 1
        } else {
            plus = 0
        }
        result.unshift(sum)
        i++
    }

    if(plus === 1){
        result.unshift(1)
    }

    return result.join("")
}

// console.log(bigSum("971268746871", "1968691623"))

// 2、数组转树形结构
const arr000 = [
    {id: 4, name: "部门3", pid: 3},
    {id: 1, name: "总部", pid: 0},
    {id: 2, name: "部门1", pid: 1},
    {id: 3, name: "部门1", pid: 1},
    {id: 5, name: "部门2", pid: 2},
    {id: 6, name: "部门2", pid: 2}
]

// 时间：O(n)，空间O(n)
function transferTree(arr) {
    let map = {}

    arr.map(item => map[item.id]=item)

    let head = null
    for(let id in map){
        let item = map[id]
        if(item.pid === 0){
            head = item
        } else {
            let parent = map[item.pid]
            parent.children = parent.children || []
            parent.children.push(item)
        }
    }

    return head
}

// console.log(transferTree(arr000))


// 3、属性转数组
const tree000 = {
    "id":1,
    "name":"总部",
    "pid":0,
    "children":[
      {
        "id":2,
        "name":"部门1",
        "pid":1,
        "children":[
          {
            "id":5,
            "name":"部门2",
            "pid":2
          },
          {
            "id":6,
            "name":"部门2",
            "pid":2
          }]
      },
      {
        "id":3,
        "name":"部门1",
        "pid":1,
        "children":[
          {
            "id":4,
            "name":"部门3",
            "pid":3
          }]
      }]
}

// 递归
function transferArray(tree) {
    let arr = []

    const plain = (node) => {
        if(node){
            arr.push({
                "id":node.id,
                "name":node.name,
                "pid":node.pid
            })
        }

        if(node.children){
            node.children.map(node=>plain(node))
        }
    }

    plain(tree)

    return arr
}

// console.log(transferArray(tree000))


// 4、拍平对象
const obj000 = {
    a: {
        b: 1,
        c: 2,
        d: {e: 5}
       },
    b: [
        1, 
        3, 
        {
            a: 2, 
            b: 3
        }
    ],
    c: 3
}

// {
//  'a.b': 1,
//  'a.c': 2,
//  'a.d.e': 5,
//  'b[0]': 1,
//  'b[1]': 3,
//  'b[2].a': 2,
//  'b[2].b': 3
//   c: 3
// }

// 递归
function flattenObj(obj) {
    let map = {}

    let setKey = (obj, str) => {
        if(Object.prototype.toString.call(obj) === "[object Array]"){
            obj.map((item, index) => {
                let sting = str + `[${index}]`
                setKey(item, sting)
            })
        } else if(Object.prototype.toString.call(obj) === "[object Object]"){
            Object.keys(obj).map(key => {
                let sting = str + (str.length>0?".":"") + key
                setKey(obj[key], sting)
            })
        } else if(typeof obj === "number" || typeof obj === "string"){
            map[str] = obj
        }
    }

    let str = ""
    setKey(obj, str)

    return map
}

// console.log(flattenObj(obj000))

// 5、将vnode转换成dom
const vnode000 = {
    tag: 'DIV',
    attrs:{
        id:'app'
    },
    children: [
      {
        tag: 'SPAN',
        children: [
          { tag: 'A', children: [] }
        ]
      },
      {
        tag: 'SPAN',
        children: [
          { tag: 'A', children: [] },
          { tag: 'A', children: [] }
        ]
      }
    ]
}

function transferVnode(vnode) {

    const createDom = (vnode, parent) => {
        if(vnode){
            let dom = document.createElement(vnode.tag)
            vnode.attrs && Object.keys(vnode.attrs).map(attr => dom.setAttribute(attr, vnode.attrs[attr]))
     
            if(vnode.children){
                vnode.children.map(node => createDom(node, dom))
            }

            if(parent){
                parent.appendChild(dom)
            }
        }
    }

    createDom(vnode, document.getElementById("root"))
}

// transferVnode(vnode000)


// 6、requestAnimationFrame 渲染大量数据

// 7、自定义实现axios
function myAxios (url) {
    return new Promise((resolve, reject)=>{
        const xhr = new XMLHttpRequest()
        xhr.open("GET", url, false)
        xhr.setRequestHeader("Content-Type", "application/json")
        xhr.onreadystatechange = function() {
            if(xhr.readyState !== 4) return
            if(xhr.status === 200 || xhr.status === 304){
                resolve(xhr.responseText)
            } else{
                reject(new Error(xhr.responseText))
            }
        }
        xhr.send()
    })
}


// 8、柯理化
// 固定参数
function add(a, b, c, d) {
    return a + b + c + d
}


function curry1(fn) {
    let length = fn.length

    return function _curry(...args1) {
        if(args1.length < length){
            return function (...args2) {
                let args = args1.concat(args2)
                return _curry(...args)
            }
        }

        return fn.call(this, ...args1)
    }
}

// let cfn = curry1(add)

// console.log(cfn(1,2,3,4))
// console.log(cfn(1)(2,3,4))
// console.log(cfn(1,2)(3,4))
// console.log(cfn(1,2,3)(4))



function add2 (...args) {
    return args.reduce((sum, number) => sum + number, 0)
}

function addCurry (fn) {
    let args = []

    const _curry = (...args1) => {
        args = args.concat(args1)
        console.log(fn(...args))
        return _curry
    }

    return _curry
}

// const cffss = addCurry(add2)

// cffss(1,2)
// cffss(1,2)(3)
// cffss(1)(2)(3)(4)

// 9、版本号排序
const version = ['0.1.1', '2.3.3', '0.302.1', '4.2', '4.3.5', '4.3.4.5']

function sortVersion(version) {
    let minArr = []

    return version.sort((a, b)=>{
        const arr1 = a.split(".")
        const arr2 = b.split(".")

        let i = 0
        while(i<arr1.length || i<arr2.length){
            const s1 = arr1[i]
            const s2 = arr2[i]

            if(!s1 || !s2){
                return arr2.length - arr1.length 
            }

            if(s1 === s2){
                i++
                continue 
            }

            return s1 - s2
        }
    })
}

console.log(sortVersion(version))


// 10、千分位转换
const num000 = 898738582
function transferNum (num) {

}

// 11、节流
function throttle(fn, wait){
    let time = 0

    return function (...args) {
        const now = new Date().getTime()
        if(now - time >= wait){
            fn.call(this, ...args)
            time = now
        }
    }
}


// 12、防抖
function debounce (fn, wait) {
    let timer = null

    return function (...args){
        if(timer){
            clearTimeout(timer)
        }
        setTimeout(()=>{
            fn.call(this, ...args)
        }, wait)
    }
}

// 13、deep copy
function deepCopy(obj) {
    const type = Object.prototype.toString.call(obj)
    if(type === "[object Array]"){
        return obj.map(item => deepCopy(item))
    }

    if(type === "[object Object]"){
        let newObj = Object.create(obj.prototype)
        for(let property in obj){
            if(obj.hasOwnProperty(property)){
                newObj[property] = deepCopy(obj[property])
            }
        }
        return newObj
    }

    if(type === "[object Function]"){
        const newFn = function (...args) {
            return obj.call(this, ...args)
        }
        newFn.prototype = Object.create(obj.prototype)
        return newFn
    }
}


// 14、promise
const SUCCEES = "SUCCEES"
const FAILED = "FAILED"
const PEDDING = "PEDDING"

class myPromise {
    constructor(fn){
        this.status = PEDDING
        this.value = null
        this.error = null
        this.successCallback = []
        this.failCallback = []

        fn(this.resolve, this.reject)
    }

    resolve = (value) => {
        if(this.status !== PEDDING){
            return
        }
        this.status = SUCCEES
        this.value = value

        while(this.successCallback.length > 0){
            this.successCallback.shift()()
        }
    }

    reject = (error) => {
        if(this.status !== PEDDING){
            return
        }
        this.status = FAILED
        this.error = error

        while(this.failCallback.length > 0){
            this.failCallback.shift()()
        }
    }

    then(successCallback, failCallback) {
        successCallback = successCallback ? successCallback : value => this.value
        failCallback = failCallback ? failCallback : error => this.error


        let p = new myPromise((resolve, reject) => {
            if(this.status === SUCCEES){
                let x = successCallback(this.value)
                this.resolveValue(x, resolve, reject)
            } else if(this.status === FAILED){
                failCallback(this.error)
            } else {
                this.successCallback.push(()=>{
                    let x = successCallback(this.value)
                    this.resolveValue(x, resolve, reject)
                })
                this.failCallback.push(()=>{
                    failCallback(this.error)
                })
            }
        })
        return p
    }


    resolveValue (x, resolve, reject) {
        if(x instanceof myPromise){
            x.then(resolve, reject)
        } else {
            resolve(x)
        }
    }

    resolve(x) {
        if(x instanceof myPromise){
            return x       
        } else {
            return new myPromise((resolve, reject)=>{
                resolve(x)
            })
        }
    }

    all(arr) {
        return new myPromise((resolve, reject)=>{

            let result = []

            const pushArr = (value) => {
                result.push(value)
                if(result.length === arr.length){
                    resolve(result)
                }
            }


            arr.map(p => {
                if(p instanceof myPromise){
                    p.then((value)=>{
                        pushArr(value)
                    }, reject)
                } else {
                    pushArr(value)
                }
            })
        })
    }


    race(arr){
        return new myPromise((resolve, reject)=>{
            let resolved = false
            arr.map(p => {
                if(p instanceof myPromise){
                    p.then(value => {
                        if(!resolved){
                            resolve(value)
                            resolved = true
                        }
                    }, reject)
                } else {
                    if(!resolved){
                        resolve(value)
                        resolved = true
                    }
                }
            })
        })
    }
}


// 15、JSON.parse
function myParse(str){
    return (new Function('return {name: "name"}'))()
}

// console.log(myParse())


// 16、JSON.stringify
// function stringify(obj) {
// }
// console.log(stringify({name: "name"}))

// 17、Object.assign
function myAssign(...args) {
    for(let i = args.length - 1; i > 0; i--){
        for(let key in args[i]){
            if(args[i].hasOwnProperty(key)){
                args[i - 1][key] = args[i][key]
            }
        }
    }

    return args[0]
}


// console.log(myAssign({name: "张三"}, {name: "李四", age: 18}))


// 18、写一个洋葱圈的compose函数
function dispatch(next) {
    console.log("before dispatch")
    next && next()
    console.log("after dispatch")
}

const disArray = [dispatch, dispatch, dispatch]
// 关键点1：递归调用dispatch
function compose(dispatchs) {
    let excute = (i) => {
        if(i < dispatchs.length){
            let dispatch = dispatchs[i]
            // 关键点2：将next作为参数调用自己，注意这里的参数是一个function
            dispatch(excute.bind(null, i + 1))
        }
    }
    excute(0)
}

// compose(disArray)



// 1、是不是回文？
const isPalindrome = (str) => {
    if(str.length<1) return false

    // 以下判断是否回文：
    let len = str.length
    let i = 0
    let j = len - 1

    while(i<=j){
        if(str[i]!==str[j]){
            return false
        }
        i++
        j--
    }

    return true
}

// 2、查找最长回文子串：递归+中心搜索法

/**
 * @param {string} s
 * @return {string}
 */
 var longestPalindrome = function(s) {
    if(s.length === 0) return ""
    if(s.length === 1) return s
    if(s.length === 2 && s[0] === s[1]){
        return s
    }

    let len = s.length
    let i = 0
    let max = ""

    while(i<len){
        let j = len

        while(j>=0){
            let str = s.slice(i, j)
            let isTrue = isPalindrome(str)
            if(isTrue) {
                max = str.length>max.length?str:max
                break
            }
            j--
        }
        i++
    }
    return max
};


console.log(longestPalindrome("babaddtattarrattatddetartrateedredividerb"))


/**
 * @param {string} s
 * @return {number}
 */
 var countSubstrings = function(s) {
    let map = {}

    // 以下收集有多少回文子串 
    let len = s.length
    let numbers = 0
    for(let i=0;i<len;i++){
        for(let j=len;j>i;j--){
            let str = s.slice(i,j)
            // 缓存，空间换时间
            let isTrue = typeof map[str] !== "undefined"?map[str]:isPalindrome(str)
            map[str] = isTrue
            if(isTrue){
                numbers++
            }
        }
    }

    return numbers
};

console.log(countSubstrings("dbabcccbcdbbbadabbdabaabcbbabaacdadcdbbbbdddbcbbbcbcabacacdaadaadcdccbacdaadadcbaacabbddabdadcabbccadacadaaacbbddaaababacaabbbacaccbcbbabddbbcccaaacbcdcabcbacdbddcdcadaaadcbccbbcabbcbdaadcbddaacacdadacbbdabcdcdadccaccdbdddddcabdbcdbaadacadadbabdcdbbadaacdbadcdabdbbcabbbdaacaaaaadcaabaaaadabaaddcaabdddcbcadcbdbbdbcbcdbadcadacbbcdccddaccccacbacaacbbdadadcacabdabadbbcdbcaaccdbdcabcadbacbccddbabbdacbcbbcbcabcacdaacccaadcdbdccabcaaacaddadcabacdccaaaddaaadbccdbbcccdddababcdbcddcbdddbbbdaadaccbcaabbcbdbadbabbacdbbbdaaccdcabddacadabcccacdabacbcdbcbdabddacacbdbcaacacaabbaaccddabaadbbaabaddbcacbacdbbbacdccabbcdbbbdbdbbcacabdddbdbaaabbcdcbabcbbbccdcdcdcaaddadccbabbacaddcaddcadcdccaccacabbaababdbbcbdcdccccccdadbdbdcdbdadcdcacdaabaacabaacdacdbaaccadbcddbdccabbcabcadcbdadbcdadbbbccacbcbbcaaaabdacbadacaadcadaacdacddcbbabdacacaabccdaccbbcbbcbcaacdabdcbcdbccdbcbcbddaacdacaaaddcaddcadccacbaddbbbccbbbcbbcbadcabbccbbaadaacacabddbdbddcadbdaaccadbcccabdcdbadbbacbcbbcdcabcddcddddabddbdabdcabdbdbbbcdbcdabbdcb"))

// 20、转换16进制到RGB
function transferColor(color) {
    const isStrValid = (str) => {
        if(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/gi.test(str)){
            return true
        }
        
        return false
    }

    const isValid = isStrValid(color)
    if(!isValid){
        throw new Error("请输入正确的颜色！")
    }

    let i = 0
    let j = 0
    let arr = []
    let str = color.replace("#", "")
    if(str.length === 3){
        while(i<3){
            arr[i] = parseInt("0x" + str[i] + str[i])
            i++
        }
    }
    if(str.length === 6){
        while(i<6){
            arr[j] = parseInt("0x" + str[i] + str[i+1])
            j++
            i = i + 2
        }
    }

    return `rgb(${arr[0]},${arr[1]},${arr[2]})`
}
console.log(transferColor("#fff"))
console.log(transferColor("#0efa66"))

// 21、转换成 16 进制
function transfer16(rgb) {
    let reg = /^rgb\([0-9]{1,3},[0-9]{1,3},[0-9]{1,3}\)$/    
    if(!reg.test(rgb)){
        return rgb
    }

    let str = rgb.replace("rgb(", "").replace(")", "")
    let rgbArr = str.split(",")

    return rgbArr.reduce((color, number) => {
        return color + Number(number).toString(16)
    }, "#")
}


console.log(transfer16("rgb(25,255,255)"))


// 22、转换各种2、10、16进制的数到10进制，parseInt
parseInt("11", 2) //3
parseInt("11", 10)//11
parseInt("11", 16)//17
parseInt("0xff")  //255

// 23、从10进制转换为各种2、10、16进制的数
Number(255).toString(16) //ff
Number(4).toString(2)    //100


// 24、并行执行Promise
function getData() {
    return new Promise((resolve, reject)=>{
        setTimeout(()=>{
            resolve("setTimeout")
        }, 1000)
    })
}


function threadLoader(promises, limit) {
    let i = 0
    let task = []

    while(i<limit){
        let promise = promises.shift()()
        task.push(promise)
        i++
    }

    Promise.race(task).then((value)=>{
        return fn()
    })
}

let promises = [
    getData,
    getData,
    getData,
    getData,
    getData,
    getData
]
threadLoader(promises, 5)


// 25、模拟setInterval
function myInterval (fn, wait) {
    let timer = null
    
    function interval () {
        fn.call(this)
        timer = setTimeout(() => {
            interval()
        }, wait)
    }

    return {
        cancel: () => {
            clearTimeout(timer)
        }
    }
}

// myInterval(()=>{
//     this.value = 6
// })

// 

// 26、嵌套数组的最大深度，只用一个参数
let qiantaoArray = [[1,[2,3,[4,5,[6]]]]]

function findDeepNum(arr) {
    if(Array.isArray(arr)){
        return arr.reduce((max, item) => {
            return Math.max(max, findDeepNum(item))
        }, 0) + 1
    }
    return 0
}

// console.log(findDeepNum(qiantaoArray))


// 27、实现一个sleep函数

// （1）同步的方式
function sleep1(wait){
    const pre = new Date().getTime()

    let time = pre
    while(time - pre < 5000){
        time = new Date().getTime()
    }
}


